package profile;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.util.ArrayList;
import java.util.List;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.IntervalMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYDifferenceRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;


/**
 *  This is a utility class that is used to create profile charts 
 */
public class ProfileChartHelper {
    
    private static final String X_AXIS_LABEL = "Position";
    
    private static float[] dashes = {5.0f, 150.0f};
    private static BasicStroke medianStroke = new BasicStroke(4.0f, 
            BasicStroke.CAP_SQUARE, 
            BasicStroke.JOIN_ROUND,
            10.0f,
            dashes,
            0f);
    
    //75% grey color to mark cropped areas
    private static final Color cropIntervalColor = new Color(128, 128, 128, 192);

    public static JFreeChart getProfileChart(Profile profile, String title, int madNumber, boolean cropAxes) {

        List<XYSeriesCollection> datasets = createProfileDatasets(profile, madNumber);
        
        XYPlot plot = createProfilePlot(profile, datasets);

        JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, true);
        
        //customize chart
        ValueAxis xAxis = chart.getXYPlot().getDomainAxis();
        if (cropAxes) {
            xAxis.setRange(profile.getCropInfo().left, profile.getCropInfo().right);
        } else {
            xAxis.setRange(0, profile.getWidth());
        }
        xAxis.setLabel(X_AXIS_LABEL);
        xAxis.setTickLabelFont(new Font("Dialog", Font.PLAIN, 16));
        ValueAxis yAxis = chart.getXYPlot().getRangeAxis();
        if (cropAxes) {
            yAxis.setRange(profile.getCroppedProfileMin(), profile.getCroppedProfileMax());
        } else {
            yAxis.setRange(profile.getProfileMin(), profile.getProfileMax());
        }
        yAxis.setLabel(profile.getYAxisLabel());
        yAxis.setTickLabelFont(new Font("Dialog", Font.PLAIN, 16));
        chart.getXYPlot().getRenderer().setSeriesPaint(0, Color.BLUE);
        chart.getXYPlot().getRenderer().setSeriesPaint(1, Color.RED);
        
        chart.getXYPlot().getRenderer().setSeriesPaint(2, Color.PINK);
        chart.getXYPlot().getRenderer().setSeriesPaint(3, Color.PINK);
        chart.getXYPlot().setBackgroundPaint(Color.WHITE);
        
        //set any crop markers
        IntervalMarker leftMarker = new IntervalMarker(0, profile.getCropInfo().left, cropIntervalColor);
        IntervalMarker rightMarker = new IntervalMarker(profile.getCropInfo().right, profile.getArr().length, cropIntervalColor);
        plot.addDomainMarker(leftMarker);
        plot.addDomainMarker(rightMarker);
        
        return chart;
    }
    
    private static XYPlot createProfilePlot(Profile profile, List<XYSeriesCollection> datasets) {
        XYItemRenderer xyRenderer = new StandardXYItemRenderer();
        XYDifferenceRenderer r1 = new XYDifferenceRenderer(
                new Color(0, 0, 0, 0), new Color(61, 100, 255, 180), false);
        
        //Set the XYDifference dataset first
        ValueAxis domainAxis = new NumberAxis();
        ValueAxis rangeAxis = new NumberAxis();
        XYPlot plot = new XYPlot(datasets.get(0), domainAxis, rangeAxis, r1);
        
        //Set the remaining dataset
        xyRenderer.setSeriesStroke(0, medianStroke);
        plot.setDataset(1, datasets.get(1));
        plot.setRenderer(1, xyRenderer);
        
        return plot;
    }
    
    private static List<XYSeriesCollection> createProfileDatasets(Profile profile, int madNumber) {
        List<XYSeriesCollection> datasets = new ArrayList<XYSeriesCollection>();
        
        //need to create a separate dataset that only contains the profile and the 
        // highest MAD so it can highlight the difference inbetween.  All other series
        // will be put on the same dataset
        XYSeriesCollection diffedCollection = new XYSeriesCollection();
        XYSeriesCollection dataset = new XYSeriesCollection();
        
        XYSeries series = new XYSeries("Profile");
        XYSeries medianSeries = new XYSeries("Median");
        //TODO: Change these to just be a DomainMarker instead of a series
        XYSeries[] madSeriesHigh = createMadSeries(profile, madNumber, true);
        XYSeries[] madSeriesLow = createMadSeries(profile, madNumber, false);
        double median = profile.getProfileMedian();
        double[] arr = profile.getArr();
        for (int i = 0; i < arr.length; i++) {
            series.add(i, arr[i]);
            medianSeries.add(i, median);
        }
        diffedCollection.addSeries(series);
        dataset.addSeries(medianSeries);
        for(XYSeries madSeries : madSeriesHigh) {
            dataset.addSeries(madSeries);
        }
        for(int i = 0; i < madSeriesLow.length; i++) {
            if(i == (madSeriesLow.length - 1)) {
                diffedCollection.addSeries(madSeriesLow[i]);
            }
            dataset.addSeries(madSeriesLow[i]);
        }
        
        datasets.add(diffedCollection); 
        datasets.add(dataset);
        return datasets;
    }
    
    /**
     * Creates an MAD series.  This method includes logic to differentiate between the MAD lines
     * that are above and below the median, which is necessary because we have to draw those
     * lines with separate XYSeries
     * 
     * @param profile
     * @param madNumber
     * @param high
     * @return
     */
    private static XYSeries[] createMadSeries(Profile profile, int madNumber, boolean high) {
        XYSeries[] madSeries = new XYSeries[madNumber];
        
        double mad = profile.getProfileMAD();
        double median = profile.getProfileMedian();
        
        for(int i = 0; i < madSeries.length; i++) {
            madSeries[i] = new XYSeries("MAD" + (i+1));
            for(int j = 0; j < profile.getArr().length; j++) {
                if(high) {
                    madSeries[i].add(j, median + ( mad * (i+1)));
                } else {
                    madSeries[i].add(j, median - ( mad * (i+1)));
                }
            }
        }
        
        return madSeries;
    }
    
}
